#include <bits/stdc++.h>
using namespace std;

class Solution {
public:
	vector<vector<int>> threeSum(vector<int>& nums) {
		vector<vector<int>> result;
		sort(nums.begin(), nums.end());
		// 找出a + b + c = 0
		// a = nums[i], b = nums[j], c = -(a + b)
		for (int i = 0; i < nums.size(); i++) {
			// 排序之后如果第一个元素已经大于零，那么不可能凑成三元组
			if (nums[i] > 0) {
				break;
			}
			if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
				continue;
			}
			unordered_set<int> set;
			/*
			  关于b的去重：
				1. c只能出现在a、b之间，因此，b在i+2的时候刚刚好留一个空给c，因此重复只会在i+2之后出现，即i+3
				2. 由（1）可以得出结论为：其实b是在第三个位置才算是 第一次遍历，b可以被利用两次，因此可以被遍历2次，一次给b，一次给c
				3. 等到第三次的时候就开始重复，不能被c给利用了
				4. 因此连续出现三次以上的b都算重复。
			  B站评论区的解释：
				  哈希法就是确定最小值a，不断增大最大值b，若c存在则必定在ab之间。
				  1，a去重
				  aaxb，从第一个a开始的组合有aab，axb，从第二个a有axb，重复
				  2，b去重
				  axbbb，第一个b结尾有axb，第二个有axb，abb，第三个axb，abb，abb。从第三个开始重复。
				  3，set定义在for里面
				  确定了a，b。c只能从ab之间取。set里只能有ab之间的数。每次更换a，set都是从零开始。定义在外面，可能会取到先前记录的数，导致最小最大值不为ab，重复。
				  定义在外面可以在for最后清空，性能稍好。
			 */
			for (int j = i + 1; j < nums.size(); j++) {
				if (j > i + 2
					&& nums[j] == nums[j-1]
					&& nums[j-1] == nums[j-2]) { // 三元组元素b去重
					continue;
				}
				int c = 0 - (nums[i] + nums[j]);
				if (set.find(c) != set.end()) {
					result.push_back({nums[i], nums[j], c});
					set.erase(c);// 三元组元素c去重
				} else {
					set.insert(nums[j]);
				}
			}
		}
		return result;
	}
};

class Test{
public:
	vector<vector<int>> threeSum(vector<int>& nums) {
		vector<vector<int>> result;
		// 1. 排序
		sort(nums.begin(),nums.end());
		for(int i = 0;i < nums.size();i++){
			// 2. 如果第一个数都大于0，那么就没有结果
			if(nums[i] > 0){
				break;
			}
			// 3. 对a去重,从第二个开始去重
			if(i > 0&&nums[i] == nums[i-1]) continue;
			
			// 4. 准备一个set
			unordered_set<int> set;
			for(int j = i+1;j< nums.size();j++){
				// 5. 对b去重
				if(j > i+2 && nums[j] == nums[j-1] && nums[j-1] == nums[j-2]) continue;
				int target = 0 - (nums[i] + nums[j]);
				if(set.find(target) != set.end()){
					result.push_back({nums[i],nums[j],target});
					set.erase(target);// 对c去重，即使a，b去重了，但是如果a+1, b-1,对应的c也有可能重复
				}else{
					set.insert(target);// 否则就把target当做c放进去
				}
			}			
		}
		return result;
	}
};

class Test2{
public:
	vector<vector<int>> threeSum(vector<int>& nums) {
		vector<vector<int>> result;
		// 1. 排序
		sort(nums.begin(),nums.end());
		for(int i = 0;i < nums.size();i++){
			if(i > 0 && nums[i] == nums[i-1]) continue;
			int left = i+1;
			int right = nums.size()-1;
			while(left < right){
				if(nums[i]+nums[left]+nums[right] > 0){
					right--;
				}else if(nums[i]+nums[left]+nums[right] < 0){
					left++;
				}else{
					result.push_back({nums[i],nums[left],nums[right]});
					while(left < right && nums[left] == nums[left+1]) left++;
					while(left < right && nums[right] == nums[right-1]) right--;
					left++;
					right--;
				}
			}
		}
		return result;
	}
};
